package com.tenbent.product.base.mybatis;

import com.tenbent.product.base.mybatis.annotations.TypeAlias;
import com.tenbent.product.base.mybatis.annotations.TypeHandler;
import com.tenbent.product.base.enums.base.BaseEnum;
import com.tenbent.product.base.enums.base.EnumPool;
import com.tenbent.product.base.utils.ClassUtil;
import com.tenbent.product.base.utils.EnumUtil;
import org.apache.commons.collections.MapUtils;
import org.dom4j.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * mybatis 配置类
 *
 * @author Randy
 *
 *         Created by ThinkPad on 2017/7/25.
 */
public class MybatisConfigResource implements Resource {

	private final static Logger logger = LoggerFactory.getLogger(MybatisConfigResource.class);

	private InputStreamResource isr;

	private List<String> entityBasePack;
	private List<String> enumBasePack;

	public InputStreamResource getResource() {
		if (isr == null) {
			isr = new InputStreamResource(createConfig());
		}
		return isr;
	}

	private InputStream createConfig() {
		Document doc = getDoc();
		Element root = doc.addElement("configuration");

		Map<String, String> aliases = getAliases();
		if (MapUtils.isNotEmpty(aliases)) {
			Element typeAliases = root.addElement("typeAliases");
			for (String alias : aliases.keySet()) {
				Element aliasNode = typeAliases.addElement("typeAlias");
				aliasNode.addAttribute("alias", alias);
				aliasNode.addAttribute("type", aliases.get(alias));
			}
		}

		Map<String, String> handlers = getTypeHandlers();
		if (MapUtils.isNotEmpty(handlers)) {
			Element typeHandlers = root.addElement("typeHandlers");
			for (String handler : handlers.keySet()) {
				Element handlerNode = typeHandlers.addElement("typeHandler");
				handlerNode.addAttribute("handler", "com.afbx.pub.framework.mybatis.typeHandler.EnumTypeHandler");
				handlerNode.addAttribute("jdbcType", handlers.get(handler));
				handlerNode.addAttribute("javaType", handler);
			}
		}

		Element pluginsNode = root.addElement("plugins");
		Element pluginNode = pluginsNode.addElement("plugin");
		pluginNode.addAttribute("interceptor", "com.github.pagehelper.PageHelper");
		Element propertyNode = pluginNode.addElement("property");
		propertyNode.addAttribute("name", "dialect");
		propertyNode.addAttribute("value", "mysql");

		logger.info(doc.asXML());
		return new ByteArrayInputStream(doc.asXML().getBytes());
	}

	private Document getDoc() {
		Document doc = DocumentHelper.createDocument();
		doc.setXMLEncoding("UTF-8");
		DocumentFactory documentFactory = new DocumentFactory();
		DocumentType docType = documentFactory.createDocType("configuration", "-//mybatis.org//DTD Config 3.0//EN",
				"http://mybatis.org/dtd/mybatis-3-config.dtd");
		doc.setDocType(docType);
		return doc;
	}

	private Map<String, String> getAliases() {
		Map<String, String> aliases = new HashMap<>();
		for (String pack : entityBasePack) {
			List<Class<?>> clazzs = ClassUtil.getClassesWithAnnotation(pack, TypeAlias.class);
			for (Class<?> clazz : clazzs) {
				TypeAlias alias = clazz.getAnnotation(TypeAlias.class);
				if (alias != null) {
					aliases.put(alias.value(), clazz.getName());
				}
			}
		}
		return aliases;
	}

	public Map<String, String> getTypeHandlers() {
		Map<String, String> handlers = new HashMap<>();
		for (String pack : enumBasePack) {
			List<Class<?>> clazzs = ClassUtil.getClassesWithAnnotation(pack, TypeHandler.class);
			for (Class<?> clazz : clazzs) {
				TypeHandler handler = clazz.getAnnotation(TypeHandler.class);
				if (handler != null) {
					handlers.put(clazz.getName(), handler.value());
					// 将枚举添加库中
					registerEnums(clazz, handler);
				}
			}
		}
		return handlers;
	}

	public void registerEnums(Class clazz, TypeHandler handler) {
		String[] columns = handler.columns();
		BaseEnum[] enums = EnumUtil.getEnums(clazz);
		for (String column : columns) {
			EnumPool.addEnums(column, enums);
		}
	}

	public void setEntityBasePack(List<String> entityBasePack) {
		this.entityBasePack = entityBasePack;
	}

	public void setEnumBasePack(List<String> enumBasePack) {
		this.enumBasePack = enumBasePack;
	}

	@Override
	public boolean exists() {
		return getResource().exists();
	}

	@Override
	public boolean isReadable() {
		return getResource().isReadable();
	}

	@Override
	public boolean isOpen() {
		return getResource().isOpen();
	}

	@Override
	public URL getURL() throws IOException {
		return getResource().getURL();
	}

	@Override
	public URI getURI() throws IOException {
		return getResource().getURI();
	}

	@Override
	public File getFile() throws IOException {
		return getResource().getFile();
	}

	@Override
	public long contentLength() throws IOException {
		return getResource().contentLength();
	}

	@Override
	public long lastModified() throws IOException {
		return getResource().lastModified();
	}

	@Override
	public Resource createRelative(String s) throws IOException {
		return getResource().createRelative(s);
	}

	@Override
	public String getFilename() {
		return getResource().getFilename();
	}

	@Override
	public String getDescription() {
		return getResource().getDescription();
	}

	@Override
	public InputStream getInputStream() throws IOException {
		return getResource().getInputStream();
	}
}
